package com.broada.apm.logic.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.lang.StringUtils;
import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order;
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.broada.apm.base.BaseLogic;
import com.broada.apm.cache.IndicatorItemManager;
import com.broada.apm.entity.MenuColumn;
import com.broada.apm.entity.MenuColumn.Action;
import com.broada.apm.entity.OrderClass;
import com.broada.apm.entity.ProtocolMenu;
import com.broada.apm.entity.TradeErrorDetail;
import com.broada.apm.indicator.api.common.ApmDataIndicatorException;
import com.broada.apm.indicator.api.model.SearchCondition;
import com.broada.apm.indicator.api.model.SearchConditionComparator;
import com.broada.apm.indicator.api.util.AggregationUtil;
import com.broada.apm.logic.IndicatorLogic;
import com.broada.apm.model.BaseResource.RESOURCE_TYPE;
import com.broada.apm.model.DictMetrics;
import com.broada.apm.model.L7moduleMenu;
import com.broada.apm.model.L7moduleMenuitem;
import com.broada.apm.model.PerformanceView;
import com.broada.apm.model.Resource;
import com.broada.apm.model.Service;
import com.broada.apm.utils.ExceptionUtils;
import com.broada.apm.utils.JsonUtil;
import com.google.common.base.Strings;

public class IndicatorLogicImpl extends BaseLogic implements IndicatorLogic {
	private final static Logger log = LoggerFactory.getLogger(IndicatorLogicImpl.class);

	@Override
	public List<ProtocolMenu> queryProtocolMenus(int l7Protocol) {
		List<ProtocolMenu> menus = new ArrayList<ProtocolMenu>();
		try {
			List<L7moduleMenu> L7moduleMenus = l7ModuleMenuService.getL7moduleMenuByProtocol(String.valueOf(l7Protocol));

			for (L7moduleMenu l7moduleMenu : L7moduleMenus) {
				ProtocolMenu protocolMenu = new ProtocolMenu();
				protocolMenu.setId(l7moduleMenu.getId());
				protocolMenu.setName(l7moduleMenu.getCaption());
				protocolMenu.setProtocol(l7moduleMenu.getModuleId());
				menus.add(protocolMenu);
			}
		} catch (Exception e) {
			ExceptionUtils.error(log, "查询自定义菜单异常", e);
		}
		return menus;
	}

	@Override
	public List<MenuColumn> queryMenuColumns(int menuId) {
		List<MenuColumn> menuColumns = new ArrayList<MenuColumn>();
		try {
			List<L7moduleMenuitem> l7moduleMenuitems = l7ModuleMenuService.getL7moduleMenuitemByMenuId(menuId);
			for (L7moduleMenuitem l7moduleMenuitem : l7moduleMenuitems) {
				MenuColumn menuColumn = new MenuColumn();
				menuColumn.setName(l7moduleMenuitem.getItemCaption());
				if (l7moduleMenuitem.getMetricId() > 0) {
					DictMetrics metric = IndicatorItemManager.getInstance().getDictMetrics(
							l7moduleMenuitem.getMetricId());
					menuColumn.setDataKey(metric.getCode());
				} else {
					menuColumn.setDataKey(l7moduleMenuitem.getFieldName());
				}
				menuColumn.setItemIndex(l7moduleMenuitem.getItemIndex());
				menuColumn.setMenuId(l7moduleMenuitem.getMenuId());
				menuColumn.setAlign(l7moduleMenuitem.getAlign());
				menuColumn.setWidth(l7moduleMenuitem.getWidth());
				menuColumn.setTransform(l7moduleMenuitem.getTransform());
				if (l7moduleMenuitem.getOpenMenu() > 0) {
					Action action = new Action();
					action.setName("showDetail");
					List<Map<String, Object>> args = new ArrayList<Map<String, Object>>();
					Map<String, Object> arg = new HashMap<String, Object>();
					arg.put("key", "next_menu_id");
					arg.put("value", l7moduleMenuitem.getOpenMenu());
					args.add(arg);
					action.setArgs(args);
					menuColumn.setAction(action);
				}
				menuColumns.add(menuColumn);
			}
		} catch (Exception e) {
			ExceptionUtils.error(log, "查询自定义菜单列异常", e);
		}
		return menuColumns;
	}

	@Override
	public L7moduleMenu queryL7moduleMenu(int menuId) {
		L7moduleMenu menu = l7ModuleMenuService.getL7moduleMenuByMenuId(menuId);
		if (menu != null) {
			List<L7moduleMenuitem> l7moduleMenuitems = l7ModuleMenuService.getL7moduleMenuitemByMenuId(menuId);
			menu.setMenuItems(l7moduleMenuitems);
		}
		return menu;
	}

	@Override
	public List<PerformanceView> queryTradeErrorCode(String applicationId, String agentId, String keyWord, int top, long startDate, long endDate) {
		List<PerformanceView> perfs = new ArrayList<PerformanceView>();
		SearchCondition con = SearchCondition.newBuilder();
		TermsBuilder aggs = AggregationUtil.getTerms("uri", "rpc", top);
		TermsBuilder errorAgg = AggregationUtil.getTerms("responseCode", "returnCode");
		errorAgg.subAggregation(AggregationUtil.getCount("count", "_index"));
		errorAgg.subAggregation(AggregationUtil.getMin("firsttime", "agentStartTime"));
		errorAgg.subAggregation(AggregationUtil.getMax("lasttime", "agentStartTime"));
		errorAgg.subAggregation(AggregationUtil.getTerms("errorMessage", "returnMessage"));
		errorAgg.subAggregation(AggregationUtil.getTerms("dimensionId", "dimensionId"));
		aggs.subAggregation(errorAgg);
		con.add(aggs);
		con.add("transactionFlag", SearchConditionComparator.IN, new String[]{"1", "3"});
		con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
		// applicationId可能是节点组id
		con.add("applicationId", SearchConditionComparator.EQUALS, applicationId);
		if (StringUtils.isNotBlank(agentId)) {
			con.add("agentId", SearchConditionComparator.EQUALS, agentId);
		} else {
			String[] serviceIds = getChildIds(applicationId);
			if (serviceIds != null && serviceIds.length > 0)
				con.add("agentId", SearchConditionComparator.IN, serviceIds);
		}
		if (StringUtils.isNotBlank(keyWord)) {
			SearchCondition searchCondition = SearchCondition.newBuilder();
			searchCondition.addOperator(SearchCondition.SearchConditionOperator.OR);
			searchCondition.add("rpc", SearchConditionComparator.LIKE, keyWord);
			searchCondition.add("returnMessage", SearchConditionComparator.LIKE, keyWord);
			searchCondition.add("returnCode", SearchConditionComparator.LIKE, keyWord);
			con.add(searchCondition);
		}
		
		try {
			perfs = queryPerfService.queryAggregationTermsPerf(con);
		} catch (ApmDataIndicatorException e) {
			ExceptionUtils.error(log, "查询性能数据失败", e);
		}
		return perfs;
	}

	private String[] getChildIds(String applicationId) {
		List<String> nodeIds = serviceService.getChildServiceIdsByApplicationId(applicationId);
		return nodeIds.toArray(new String[nodeIds.size()]);
	}

	private String[] getNodeIds(String nodeId) {
		Set<String> nodeIds = new HashSet<String>();
		Service ser;
		if (nodeId.contains("-")) {
			ser = serviceService.getServiceById(nodeId);
			if (ser.getNodeType() == Service.NodeTypeCluster) {
				List<Service> chlidren = serviceService.getChildServiceByClusterId(nodeId);
				for (Service service : chlidren) {
					nodeIds.add(service.getId());
				}
			} else {
				nodeIds.add(nodeId);
			}
		} else {
			
			ser = serviceService.getDisServiceById(nodeId);
			nodeIds.add(nodeId);
		}
		return nodeIds.toArray(new String[nodeIds.size()]);
	}
	
	@Override
	public List<PerformanceView> queryMenuData(String applicationId, String agentId, int menuId, String dataKey, String dataId,
			String keyWord, int pageIndex, int pageSize, long startDate, long endDate, String orderBy, boolean desc) throws Exception {
		L7moduleMenu l7moduleMenu = queryL7moduleMenu(menuId);
		if (l7moduleMenu == null)
			throw new Exception("菜单不存在");
		List<L7moduleMenuitem> menuItems = l7moduleMenu.getMenuItems();
		if (menuItems == null || menuItems.isEmpty())
			throw new Exception("菜单不存在数据列");

		List<PerformanceView> perfs = new ArrayList<PerformanceView>();
		if (StringUtils.isNotBlank(l7moduleMenu.getFieldGroupby())) {
			String fieldGroupby = "domainId".equals(l7moduleMenu.getFieldGroupby()) ? "endPoint" : "rpc";
			SearchCondition con = SearchCondition.newBuilder();
			con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
			con.add("applicationId", SearchConditionComparator.EQUALS, applicationId);
			con.add("agentId", SearchConditionComparator.EQUALS, agentId);

			// 设置自定义菜单where条件
			if (StringUtils.isNotBlank(l7moduleMenu.getFieldWhere())) {
				String fieldWhere = l7moduleMenu.getFieldWhere();
				String[] kv = fieldWhere.split("=");
				if (kv.length < 2) {
					throw new Exception("自定义菜单where字段格式不正确：" + fieldWhere);
				}
				con.add(kv[0], SearchConditionComparator.EQUALS, kv[1]);
			}
			// 设置详情页面过滤条件
			if (StringUtils.isNotBlank(dataId) && StringUtils.isNotBlank(dataKey)) {
				con.add(dataKey, SearchConditionComparator.EQUALS, dataId);
			}
			// 关键字查询
			if (StringUtils.isNotBlank(keyWord)) {// 对groupby字段进行关键字查找
				con.add(fieldGroupby, SearchConditionComparator.LIKE, keyWord);
			}
			
			TermsBuilder aggs = AggregationUtil.getTerms("resourceId", fieldGroupby, 0);
			aggs.subAggregation(AggregationUtil.getCount("system/throughput/total", "_index"));
			aggs.subAggregation(AggregationUtil.getAvg("system/response_time/avg", "elapsed"));
			aggs.subAggregation(AggregationUtil.getFilter("system/error/count", "transactionFlag", new String[]{"1", "3"}));
			if(!Strings.isNullOrEmpty(orderBy)){
				if ("system/success/rate".equals(orderBy)) {
					aggs.order(Order.compound(Order.aggregation("system/throughput/total", !desc), Order.aggregation("system/error/count", desc)));
				} else {
					aggs.order(Order.aggregation(orderBy, !desc));
				}
			    
			} else {
				aggs.order(Order.aggregation("system/throughput/total", desc));
			}
			con.add(aggs);
			
			try {
				perfs = queryPerfService.queryAggregationTermsPerf(con);
				for (PerformanceView perf : perfs) {
					perf.put("system/success/rate",
							(perf.get("system/throughput/total") == null
									|| Double.parseDouble(perf.get("system/throughput/total").toString()) == 0d)
											? "-"
											: (Double.parseDouble(perf.get("system/throughput/total").toString())
													- Double.parseDouble(perf.get("system/error/count").toString()))
													/ Double.parseDouble(perf.get("system/throughput/total").toString()) * 100);
				}
			} catch (ApmDataIndicatorException e) {
				ExceptionUtils.error(log, "查询性能数据失败", e);
			}
		}
		return perfs;
	}

	private RESOURCE_TYPE getResourceType(String groupFiled) {
		if ("applicationId".equalsIgnoreCase(groupFiled))
			return RESOURCE_TYPE.APPLICATION;

		if ("serviceId".equalsIgnoreCase(groupFiled))
			return RESOURCE_TYPE.SERVICE;

		if ("domainId".equalsIgnoreCase(groupFiled))
			return RESOURCE_TYPE.DOMAIN;

		if ("resourceId".equalsIgnoreCase(groupFiled))
			return RESOURCE_TYPE.RESOURCE;

		return null;
	}

	private List<PerformanceView> convert(List<L7moduleMenuitem> menuItems, List<PerformanceView> perfs) {
		List<PerformanceView> perfList = new ArrayList<PerformanceView>();
		for (L7moduleMenuitem item : menuItems) {
			if (StringUtils.isBlank(item.getMappingClass()))
				continue;
			List<String> resourceIds = new ArrayList<String>();
			for (PerformanceView performanceView : perfs) {
				if (performanceView.isKey()) {
					continue;
				}
				resourceIds.add(performanceView.getResourceId());
			}
			
			List<Resource> resourceList = resourceService.getResourceByIds(resourceIds);
			Map<String, Resource> resourceMap = new HashMap<String, Resource>();
			if (resourceList != null && !resourceList.isEmpty()) {
				for (Resource res : resourceList) {
					resourceMap.put(res.getId(), res);
				}
			}
			if ("domainId".equals(item.getFieldName())) {
				for (PerformanceView performanceView : perfs) {
					String fieldName = item.getFieldName();
					if (performanceView.containsKey(fieldName)) {
						performanceView.put(fieldName, convert(resourceMap, performanceView.get(fieldName), item.getItemCaption()) == null ? performanceView.get(fieldName) : convert(resourceMap, performanceView.get(fieldName), item.getItemCaption()));
						perfList.add(performanceView);
					}
				}
			} else {
				for (PerformanceView performanceView : perfs) {
					String fieldName = item.getFieldName();
					if (performanceView.containsKey(fieldName)) {
						if (convert(resourceMap, performanceView.get(fieldName), item.getItemCaption()) == null) {
							continue;
						}
						performanceView.put(fieldName, convert(resourceMap, performanceView.get(fieldName), item.getItemCaption()));
						perfList.add(performanceView);
					}
				}
			}
		}
		return perfList;
	}
	
	private Object convert(Map<String, Resource> resourceMap, Object value, String itemCaption) {
		Resource res = null;
		if (resourceMap != null && !resourceMap.isEmpty()) {
			res = resourceMap.get((String) value);
		}
		if (res != null) {
			if("函数名".equals(itemCaption)) {
				return res.getSoapFunction() == null ? res.getUri() : res.getSoapFunction();
			}
			return res.getUri();
		}
		return null;
	}
	
	private List<PerformanceView> sortByCondition(List<PerformanceView> staticPerf, String orderBy, String orderDirection) {
		
		List<OrderClass> list = new ArrayList<OrderClass>();
		List<PerformanceView> staticPerf2 = new ArrayList<PerformanceView>();
		for (int i=0;i<staticPerf.size();i++) {
			OrderClass order= new OrderClass();
			PerformanceView performanceView = staticPerf.get(i);
			if (performanceView.get(orderBy).toString().equals("NaN")) {
				performanceView.put(orderBy, 0.0);
			}
			if ("-".equals(performanceView.get(orderBy))) {
				performanceView.put(orderBy, 0.0);
			}
			order.setPerformanceView(performanceView);
			order.setOrder("-".equals(performanceView.get(orderBy)) ? 0.0 : Double.parseDouble(performanceView.get(orderBy).toString()));
			list.add(order);
		}
		
		if (orderDirection.equals("asc")) {
			Collections.sort(list, new Comparator<OrderClass>() {
				public int compare(OrderClass arg0, OrderClass arg1) {
					return arg0.getOrder().compareTo(arg1.getOrder());
				}
			});
		} else {
			Collections.sort(list, new Comparator<OrderClass>() {
				public int compare(OrderClass arg0, OrderClass arg1) {
					return arg1.getOrder().compareTo(arg0.getOrder());
				}
			});
		}
		
		for (int i=0;i<list.size();i++) {
			OrderClass orderClass= list.get(i);
			staticPerf2.add(i, orderClass.getPerformanceView());
		}
		return staticPerf2;
		
	}


	/**
	 * @Title: reverse
	 * @Description: 通过groupBy找到对应的MappingClass,然后对keyword内容进行反向转换
	 * @param menuItems
	 * @param groupBy
	 * @param appId
	 * @param nodeId
	 * @param keyword
	 * @return Object[]
	 * @throws
	 */
	private Object[] reverse(List<L7moduleMenuitem> menuItems, String groupBy, String appId, String nodeId,
			String keyword) {
		Object[] result = null;
		for (L7moduleMenuitem item : menuItems) {
			if (StringUtils.equals(item.getFieldName(), groupBy)) {
				if (StringUtils.isNotBlank(item.getMappingClass())) {
					String serviceId = nodeId.substring(nodeId.lastIndexOf("-") + 1); // MySQL库中存resource，belongTo只存serviceId，不包含appId
					result = reverse(appId, serviceId, keyword);
				}
				break;
			}
		}
		return result;
	}
	
	private Object[] reverse(String appId, String serviceId, String keyWord) {
		Resource res = new Resource();
		res.setUri(keyWord);
		List<Resource> ress = resourceService.getResourcesByKeyWord(res);
		Set<String> ids = new HashSet<String>();
		for (Resource resource : ress) {
			ids.add(resource.getId());
		}

		return ids.toArray(new String[ids.size()]);
	}
	
	private void setId(List<PerformanceView> perfs, String field) {
		for (PerformanceView performanceView : perfs) {
			performanceView.put("id", performanceView.get(field));
		}
	}

	@Override
	public List<PerformanceView> queryTradeErrorStacks(String applicationId, String agentId, String uri, String errorCode,
			long startDate, long endDate) {
		SearchCondition con = SearchCondition.newBuilder();
		con.add("transactionFlag", SearchConditionComparator.IN, new String[]{"1", "3"});
		con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
		// applicationId可能是节点组id
		con.add("applicationId", SearchConditionComparator.EQUALS, applicationId);
		if (StringUtils.isNotBlank(agentId)) {
			con.add("agentId", SearchConditionComparator.EQUALS, agentId);
		} else {
			String[] serviceIds = getChildIds(applicationId);
			if (serviceIds != null && serviceIds.length > 0)
				con.add("agentId", SearchConditionComparator.IN, serviceIds);
		}
		if (StringUtils.isNotBlank(uri))
			con.add("rpc", SearchConditionComparator.EQUALS, uri);
		if (StringUtils.isNotBlank(errorCode))
			con.add("dimensionId", SearchConditionComparator.EQUALS, errorCode);
		
		TermsBuilder aggs = AggregationUtil.getTerms("errorId", "spanId", 1000);
		con.add(aggs);
		List<PerformanceView> errors = null;
		try {
			errors = queryPerfService.queryAggregationTermsPerf(con);
		} catch (ApmDataIndicatorException e) {
			ExceptionUtils.error(log, "查询错误交易列表异常", e);
		}
		return errors;
	}

	@Override
	public void updateResShowIndicator(String targetId, String panelId, List<Integer> metricIds) throws Exception {
		if(metricIds!=null && !metricIds.isEmpty())
			try {
				indicatorItemService.updateResShowIndicator(targetId, panelId, metricIds);
			} catch (Exception e) {
				ExceptionUtils.error(log, "保存节点需要显示的自定义指标项关系", e);
				throw e;
			}
	}

	@Override
	public TradeErrorDetail getErrorDetail(String errorId, long startDate, long endDate) throws Exception {
		SearchCondition con = SearchCondition.newBuilder();
		con.add("transactionFlag", SearchConditionComparator.IN, new String[]{"1", "3"});
		con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
		con.add("spanId", SearchConditionComparator.EQUALS, errorId);
		List<PerformanceView> errorDetail = queryPerfService.queryPerformanceDetail(con);
		TradeErrorDetail detail = new TradeErrorDetail();
		if (errorDetail != null && !errorDetail.isEmpty()) {
			PerformanceView pv = errorDetail.get(0);
			@SuppressWarnings("unchecked")
			TreeMap<String, Object> map = (TreeMap<String, Object>) JsonUtil.json2obj(pv.get("l7Message").toString(), TreeMap.class);
			if(map != null)
				detail.setFields(map);
			return detail;
		}
		return null;
	}
	
}
